<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <title>AJ Framework-基于 Java 接口的控制器</title>
        <meta name="description" content="AJ Framework 一个基于 SpringMVC 构建的轻量级框架，旨在增强 SpringMVC 并使其更具 SpringBoot 的特性。它拥有许多小型组件，非常易于使用。" />
        <meta name="keywords" content="AJ Framework, ajaxjs, ajaxjs framework, java framework, web framework" />
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@200..900&family=Noto+Serif:ital,wght@0,100..900;1,100..900&display=swap&family=Noto+Sans+SC:wght@100..900&display=swap" />
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@100..900&family=Noto+Serif:ital,wght@0,100..900;1,100..900&display=swap" /> 
        <link rel="stylesheet" href="https://framework.ajaxjs.com/static/new-ui/css/common.css" />
        <link rel="stylesheet" href="https://iam.ajaxjs.com/asset/main.css"/>
        <link rel="icon" type="image/x-icon" href="https://framework.ajaxjs.com/aj-logo/logo.ico"/>
        <script src="https://framework.ajaxjs.com/static/aj-docs/common.js"></script>
        <script>
            var _hmt = _hmt || [];
            (function() {
              var hm = document.createElement("script");
              hm.src = "https://hm.baidu.com/hm.js?a3dec1d9b9415edd86b54c1fb3d02f19";
              var s = document.getElementsByTagName("script")[0];
              s.parentNode.insertBefore(hm, s);
            })();
        </script>
    </head>
    <body>
        <nav>
            <div>
                <div class="links">
                    <a href="/">🏠 首页</a>
                    | ⚙️ 源码:
                    <a target="_blank" href="https://github.com/lightweight-component/aj-framework">Github</a>/<a target="_blank" href="https://gitcode.com/lightweight-component/aj-framework">Gitcode</a>
                    |
                    <a href="/common/contact">✉️ 联系</a>
                </div>
                <h1>
                    <img src="https://framework.ajaxjs.com/aj-logo/logo.png" style="vertical-align: middle;height: 45px;margin-bottom: 6px;" />
                    AJ Framework
                </h1>
                <h3>轻量级 Java 快速开发框架
                </h3>
            </div>
        </nav>
        <div>
            <menu>
                
                <ul>
                    <li class="selected">
                        <a href="/">首页</a>
                    </li>
                </ul>
                <h3>框架基本用法</h3>
                <ul>
                    <li>
                        <a href="/framework/init">初始化一个项目</a>
                    </li>
                    <li>
                        <a href="/framework/controller">简化控制器为接口</a>
                    </li>
                    <li>
                        <a href="/framework/unified-return">统一对象返回</a>
                    </li>
                    <li>
                        <a href="/framework/unified-ex">统一异常处理</a>
                    </li>
                    <li>
                        <a href="/framework/package">打包与部署</a>
                    </li>
                </ul>
                <h3>常用组件</h3>
                <ul>
                    <li>
                        <a href="/component/db">数据库访问</a>
                    </li>
                    <li>
                        <a href="/component/bean-validator/">Bean 实体校验</a>
                    </li>
                    <li>
                        <a href="/component/security">安全组件、用户系统</a>
                    </li>
                    <li>
                        <a href="/component/lite-c/">使用简单本地缓存</a>
                    </li>
                  <li>
                       <a href="/component/cache/">使用多级缓存</a>
                   </li>
                </ul>

                <h3>其他</h3>
                <ul>
                    <li><a href="/common/contact">联系</a></li>
                </ul>
            </menu>
            <article class="aj-text chinese">
                <h1>基于 Java 接口的控制器</h1>
<p>问题的提出：一般情况下，我们是这样定义一个 Spring Boot/MVC 控制器的：</p>
<pre><code class="language-java">@PostMapping(&quot;/save/{id}&quot;)
@ResponseBody
public Book save(@RequestBody Book book, @PathVariable int id) {
    // 调用你的业务类
}
</code></pre>
<p>一直如此没啥问题。但我们渐渐发现，这个 Controller 里面只有一行调用 Service 的方法……于是我们考虑能不能把这一行代码都省呢？——答案是肯定的！我们就来看看怎么做。</p>
<h2>把接口作控制器</h2>
<p>Spring MVC 5.1 引入了 <a href="https://github.com/spring-projects/spring-framework/wiki/What%27s-New-in-Spring-Framework-5.x#general-web-revision-1">新功能</a>：</p>
<blockquote>
<p>Controller parameter annotations get detected on interfaces as well: Allowing for complete mapping contracts in controller interfaces.<br>
在接口上的控制器注解也能检测到，并自动映射到实现类上。</p>
</blockquote>
<p>说白了就是控制器只是个 Java Interface，对应的实现却不是控制器，而是业务类。这有点控制器与业务类合二为一的味道。实际上为我们减少不少编码量，还是相当值得使用的。这样相当于简化了 Controller 编写。实际上 Spring Cloud 的 OpenFeign 技术也是这套技术，公共定义一套接口，既让消费端使用接口描述 RPC，又让控制器类实现这个接口，异曲同工。</p>
<p>我们用一个用户的例子来看看。首先定义控制器，这里提供了相关的 MVC 的注解。</p>
<pre><code class="language-java">import com.ajaxjs.user.model.User;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(&quot;/user&quot;)
public interface UserController {
    @GetMapping(&quot;/{id}&quot;)
    User info(@PathVariable Long id);

    @PostMapping
    Long create(@RequestBody User user);

    /**
     * 检查用户某个值是否已经存在一样的值
     *
     * @param field 字段名，当前只能是 username/email/phone 中的任意一种
     * @param value 字段值，要校验的值
     * @return 是否已经存在一样的值，true 表示存在
     */
    @GetMapping(&quot;/checkRepeat&quot;)
    Boolean checkRepeat(@RequestParam String field, @RequestParam Object value);

    @PutMapping
    Boolean update(@RequestBody User user);

    @DeleteMapping(&quot;/{id}&quot;)
    Boolean delete(@PathVariable Long id);
}
</code></pre>
<p>接着我们实现这个接口，特别地，这是个业务类。当然你也可以说他是控制器类，但这样就失去简化的意义了，只是把之前类上的注解搬到接口身上，并没有简化控制器。我们看看实现类：</p>
<pre><code class="language-java">import com.ajaxjs.data.CRUD;
import com.ajaxjs.data.entity.CrudUtils;
import com.ajaxjs.framework.entity.BaseEntityConstants;
import com.ajaxjs.sass.SaasUtils;
import com.ajaxjs.user.controller.UserController;
import com.ajaxjs.user.model.User;
import org.springframework.stereotype.Service;

import javax.validation.Valid;

@Service
public class UserService implements UserController {
    @Override
    public User info(Long id) {
        String sql = &quot;SELECT * FROM user WHERE stat != 1 AND id = ?&quot;;
        sql = SaasUtils.addTenantIdQuery(sql);

        return CRUD.info(User.class, sql, id);
    }

    @Override
    public Long create(@Valid User user) {
        if (checkRepeat(&quot;username&quot;, user.getUsername()))
            throw new IllegalArgumentException(&quot;用户的登录名&quot; + user.getUsername() + &quot;重复&quot;);

        return CRUD.create(user);
    }

    @Override
    public Boolean checkRepeat(String field, Object value) {
        String sql = &quot;SELECT * FROM user WHERE stat != 1 AND &quot; + field + &quot; = ?&quot;;
        sql = SaasUtils.addTenantIdQuery(sql);
        sql += &quot;LIMIT 1&quot;;

        return CRUD.info(sql, value) != null;
    }

    @Override
    public Boolean update(User user) {
        CrudUtils.checkId(user);
        return CRUD.update(user);
    }

    @Override
    public Boolean delete(Long id) {
        // 逻辑删除
        User user = new User();
        user.setId(id);
        user.setStat(BaseEntityConstants.STATUS_DELETED);

        return update(user);
    }
}
</code></pre>
<p>这有点控制器与业务类合二为一的味道。实际上为我们减少不少编码量，还是相当值得使用的。</p>

            </article>
        </div>
        <footer>
             开源框架 <a href="https://framework.ajaxjs.com" target="_blank">AJ-Framework</a> 首页。联系方式：
             frank@ajaxjs.com，<a href="https://blog.csdn.net/zhangxin09" target="_blank">作者博客</a>
             <br />
             <br />
             Copyright © 2025 Frank Cheung. All rights reserved.
         </footer>
    </body>
</html>